﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Weka
{
    internal struct FieldInfo
    {
        //FieldId 0 - 95
        //First 7bit used for 
        //                  - 0 to 95 Option A
        //                  - 96 to 127 option B (need 3 more bits to map this to fieldId of 0 to 95)

        //Next 9bit gives 
        //                  - 0 to 511 (for Option A)
        //                  - 512 to 575 (for Option B) (3bits are used for fieldId)
        //this gives us
        //                  - 0   to 239  maps 0 to 239 for MessageId
        //                  - 240 to 479  maps 0 to 239 for MessageId As list
        //                  - 480 to 575  maps to  0 to 95 DataType


        private UInt16 _data;
        public FieldInfo(FieldAttribute field, DataType dataType)
        {
            byte dt = (byte)dataType;
            if (dt < 32)
            {
                //Option A
                _data = (UInt16)((field.Id << 9) | (480 + dt));
            }
            else
            {
                //Option B
                var r = ((field.Id >> 3) + 96);
                _data = (UInt16)((r << 9) | ((field.Id & 0x07) << 6) | (dt - 32));
            }

        }
        public FieldInfo(FieldAttribute field, byte messageId, bool isList)
        {
            _data = (UInt16)((field.Id << 9) | (isList ? messageId + 240 : messageId));
        }
        public FieldInfo(Stream stream)
        {
            int b1 = stream.ReadByte();
            int b2 = stream.ReadByte();
            _data = (UInt16)(b1 << 8 | b2);
        }
        public void Write(Stream stream)
        {
            stream.WriteByte((byte)(_data >> 8));
            stream.WriteByte((byte)(_data & 0xFF));
        }

        private int Top7Bits { get { return (_data >> 9); } }
        private int Bottom9Bits { get { return (_data & 0x01FF); } }
        private int Bottom6Bits { get { return (_data & 0x003F); } }
        private bool IsOptionA { get { return (Top7Bits < 96); } }
        public FieldType FieldType
        {
            get
            {
                if (!IsOptionA) return FieldType.PrimaryData;
                if (Bottom9Bits > 479) return FieldType.PrimaryData;
                if (Bottom9Bits < 240) return FieldType.Message;
                return FieldType.MessageList;
            }
        }
        public DataType DataType { get { return (DataType)(byte)((IsOptionA) ? (Bottom9Bits - 480) : (Bottom6Bits + 32)); } }
        public byte MessageId
        {
            get
            { //Option A (there is no Option B)
                return (byte)((Bottom9Bits < 240) ? Bottom9Bits : (Bottom9Bits - 240));
            }
        }
        public byte FieldId
        {
            get
            {
                if (IsOptionA)
                {
                    return (byte)Top7Bits;
                }
                else
                {
                    var t1 = (((_data >> 9) - 96) << 3);
                    var t2 = ((_data >> 6) & 0x07);
                    return (byte)(t1 | t2);
                }
            }
        }
        public override string ToString()
        {
            var mt = this.FieldType;
            if (mt == FieldType.PrimaryData) return "Field: " + FieldId.ToString() + ": " + DataType;
            if (mt == FieldType.Message) return "Field: " + FieldId.ToString() + ": Message:" + MessageId;
            return "Field: " + FieldId.ToString() + ": MessageList: " + MessageId;
        }
    }

    internal enum FieldType { PrimaryData, Message, MessageList }
}
